package service_logger

import (
	"context"
	com_model "elflog/common/com-model"
	"elflog/common/errs"
	"elflog/common/utils/pswd_util"
	model2 "elflog/module/module-account/model"
	"elflog/module/module-log/model"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// useful field bind ...
type CreateLogReqModel struct {
	AppName    string    `json:"app_name"`
	AppVersion string    `json:"app_version"`
	AppHost    string    `json:"app_host"`
	RemoteIp   string    `json:"remote_ip"`
	ModuleName string    `json:"module_name"`
	FuncName   string    `json:"func_name"`
	ClientId   string    `json:"client_id"`
	Time       time.Time `json:"time"`
	TraceId    string    `json:"trace_id"`
	UserId     string    `json:"user_id"`
	Message    string    `json:"message"`
	Level      int       `json:"level"` // level
}

func (a *LoggerService) checkSignTimeStampAndUnmarshalData(ctx context.Context, timeStamp int64, key, sign, data string) (*CreateLogReqModel, *model2.AccountLogger, error) {
	log := a.log.WithField("module", "loggerService")

	timeInterval := time.Now().Unix() - timeStamp
	if timeInterval > 5 || timeInterval < -5 {
		log.Error("time stamp is err")
		return nil, nil, errs.NewParamError().WithMsg("timestamp is err")
	}
	logger, err := a.moduleAccount.GetLoggerByKey(ctx, key)
	if err != nil {
		return nil, nil, err
	}
	if logger == nil {
		return nil, nil, errs.NewParamError().WithMsg("no this account logger")
	}
	signed := pswd_util.Md5(data + strconv.FormatInt(timeStamp, 10) + logger.Secret)
	if sign != signed {
		return nil, nil, errs.NewParamError().WithMsg("err on sign")
	}

	logDataMap := make(map[string]interface{}, 4)
	err = json.Unmarshal([]byte(data), &logDataMap)
	if err != nil {
		return nil, nil, errs.NewParamError().WithError(err)
	}
	var getMapValStr = func(m map[string]interface{}, key string) string {
		if m == nil || len(m) <= 0 || key == "" {
			return ""
		}
		val, ok := m[key]
		if ok && val != nil {
			return fmt.Sprintf("%v", val)
		} else {
			return ""
		}
	}
	var formatTimeOrNow = func(timeStr string) time.Time {
		if timeStr == "" {
			return time.Now()
		}
		parseTime, err := time.Parse(time.RFC3339Nano, timeStr)
		if err != nil {
			return time.Now()
		}
		return parseTime
	}
	var parseLogLevel = func(lvl string) int {
		// 0-6 PanicLevel FatalLevel ErrorLevel WarnLevel InfoLevel DebugLevel TraceLevel
		switch strings.ToLower(lvl) {
		case "panic":
			return 0
		case "fatal":
			return 1
		case "error":
			return 2
		case "warn", "warning":
			return 3
		case "info":
			return 4
		case "debug":
			return 5
		case "trace":
			return 6
		}
		return 7 // unknown
	}
	strategy := model2.NewFieldNameStrategy(logger.FieldNameStrategy)
	reqParam := &CreateLogReqModel{
		AppName:    getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.AppName)),
		AppVersion: getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.AppVersion)),
		AppHost:    getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.AppHost)),
		RemoteIp:   getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.RemoteIp)),
		ModuleName: getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.ModuleName)),
		FuncName:   getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.FuncName)),
		ClientId:   getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.ClientId)),
		Time:       formatTimeOrNow(getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.Time))),
		TraceId:    getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.TraceId)),
		UserId:     getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.UserId)),
		Message:    getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.Message)),
		Level:      parseLogLevel(getMapValStr(logDataMap, strategy.GetOriginFieldName(com_model.Level))),
	}
	return reqParam, logger, nil
}

// 添加一个日志 包括key sign 检查 用户存在性检查
func (a *LoggerService) PushLog(ctx context.Context, timeStamp int64, key, sign, data string) error {
	log, logger, err := a.checkSignTimeStampAndUnmarshalData(ctx, timeStamp, key, sign, data)
	if err != nil {
		return err
	}
	err = a.moduleLog.PushLog(ctx, &model.CreateLogModel{
		AccountId:  logger.AccountId,
		LoggerId:   logger.Id,
		LoggerName: logger.Name,
		AppName:    log.AppName,
		AppVersion: log.AppVersion,
		AppHost:    log.AppHost,
		RemoteIp:   log.RemoteIp,
		ModuleName: log.ModuleName,
		FuncName:   log.FuncName,
		ClientId:   log.ClientId,
		Time:       log.Time,
		TraceId:    log.TraceId,
		UserId:     log.UserId,
		Message:    log.Message,
		Content:    data,
		Level:      log.Level,
	})
	return err
}
